<?php
// +----------------------------------------------------------------------
// | 杭州十禾信息科技有限公司
// +----------------------------------------------------------------------
// | Copyright (c) 2013-2015 http://www.shelpe.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: LeePuman <ysyzlsm@163.com>
// +----------------------------------------------------------------------

/**
 * 卡位模块（抢排名）执行部分
 * Author: Boya
 */
require_once APP_PATH . 'Lib/Library/Ranking.class.php';

class RankingTaskAction extends ClientAction
{
	//初始化函数
	function _initialize()
	{
		$this->ranking = new Ranking();
	}

	/**
	 * 实时获取正在进行进价的关键词状态
	 * Author: Kungu
	 * @param  int 	$tactics_id 	策略编号
	 * @param  int 	$keyword_id 	关键词编号
	 * @return int  当前状态
	 */
	private function getTacticsDoing($tactics_id, $keyword_id) 
	{
		$doing = M('want_rank')->field('doing')->where("tactics_id = {$tactics_id} AND keyword_id = {$keyword_id}")->find();
		if($doing)
			return (int)$doing['doing'];
		// 有可能已经删除这个关键词了
		return 1;
	}

	/**
	 * 更新正在进行进价的关键词状态/更新正在进行的策略的状态
	 * Author: Kungu
	 * @param  string/array 	$tactics_id  	策略编号（组）
	 * @param  array  			$saveData   	需要写如数据库的字段（值）
	 * @param  string 			$table      	表名称
	 * @param  string/array 	$keyword_id 	关键词编号（组）
	 * @return void
	 */
	private function changeStatusTactics($tactics_id, $saveData = array(), $table = 'tactics', $keyword_id)
	{
		if(!$saveData)
			return false;
		$where = is_array($tactics_id) ? 'tactics_id IN (' . implode(',', $tactics_id) . ')' : "tactics_id = {$tactics_id}";
		if(strpos($table, 'want_rank') !== false) {
			$where .= is_array($keyword_id) ? ' AND keyword_id IN (' . implode(',', $keyword_id) . ')' : " AND keyword_id = {$keyword_id}";
			M('want_rank')->where($where)->save($saveData);
		} else {
			M('tactics')->where($where)->save($saveData);
		}
	}

	//执行卡位任务
	function doTacticsTask()
	{
		$ret = false; // 返回任务结果

		//tactics与users表连接查询得所有的用户id和authorized_to
		$tactics = M('tactics');
		$userInfos = $tactics->field('DISTINCT(tactics.user_id),users.authorized_to')
							 ->join('LEFT JOIN users ON tactics.user_id = users.user_id')
							 ->select();
		if($userInfos) {
			//模拟登录
			$userMdl = new UserModel();
			foreach ($userInfos as $userInfo) {
				try {
						$userMdl->setAuth($userInfo['user_id'], $userInfo['authorized_to']);
						$this->me = session('me');
						$this->manager = session('?manager') ? session('manager') : null;
						$this->doTactics();  //执行策略
					} catch (Exception $e) {
						$this->ranking->printError($e);
						$this->logError($e->getMessage());
						$this->logException($e, true);
						continue;
					}
				}
			$ret = true;
		} else {
			$ret = 'no_more';
		}
		
		return $ret;
	}

	//执行卡位策略
	function doTactics()
	{
		try{
			//从数据库中取出所有卡位策略
			$ranking_info_arr = $this->ranking->getRankInfo(new GetRankExecute($this->me['user_id']));
			// $ranking_info_arr = $this->ranking->getRankInfo(new GetRankExecute($userId));
			$this->_execute($ranking_info_arr);


		}catch(Exception $e){
			$this->ranking->printError($e);
			$this->logError($e->getMessage());
			$this->logException($e, true);
			$this->display_json(array('total_items' => 0, 'items' => $this->ranking->getErrorMessage($e)));
		}
	}

	private function _execute($ranking_info_arr)
	{
		$this->_doExecute($ranking_info_arr);
		$this->_doExecute($this->_TacticsSwitch($ranking_info_arr));  //转换溢价再执行一次
	}

	//转换策略,溢价是负该为正,正改为负
	private function _TacticsSwitch($ranking_info_arr)
	{
		if ($ranking_info_arr) {
			foreach ($ranking_info_arr as $key => &$ranking_info) {
				if(preg_match('/^(-\d+)(\.\d+)?\%?$/',$ranking_info['premium'])) {  //负
					$ranking_info['premium'] = substr($ranking_info['premium'], 1);
				}else {
					$ranking_info['premium'] = '-'.$ranking_info['premium'];
				}
			}
		}
		return $ranking_info_arr;
	}

	private function _doExecute($ranking_info_arr)
	{
		// dump('before_filter');
		// dump($ranking_info_arr);
		// file_put_contents (RUNTIME_PATH.'Logs/'.date("y_m_d").'_mlkr.log' , 'before_filter'.' '. date ( "Y-m-d H:i:s" ). " \r\n\r" , FILE_APPEND);
		// file_put_contents (RUNTIME_PATH.'Logs/'.date("y_m_d").'_mlkr.log' , print_r($ranking_info_arr,true).' '. date ( "Y-m-d H:i:s" ). " \r\n\r" , FILE_APPEND);
		

		if ($ranking_info_arr) {

			$campaign = '';
			$tactics_ids = '';
			$tactics_id_campaign = '';
			$tactics_keyword_iids = array();
			$success_data = '';

			//1.过滤时间,出价不合适的
			$this->_TacticsTimePriceFilter($ranking_info_arr);

			// dump('first_filter(time&price)');
			// dump($ranking_info_arr);

			if (empty($ranking_info_arr)) {
				return true;
			}

			//2.过滤排名不合适的
			$this->_TacticsRankFilter($ranking_info_arr);

			// dump('second_filter(rank)');
			// dump($ranking_info_arr);
			// file_put_contents (RUNTIME_PATH.'Logs/'.date("y_m_d").'_mlkr.log' , 'second_filter(rank)'.' '. date ( "Y-m-d H:i:s" ). " \r\n\r" , FILE_APPEND);
			// file_put_contents (RUNTIME_PATH.'Logs/'.date("y_m_d").'_mlkr.log' , print_r($ranking_info_arr,true).' '. date ( "Y-m-d H:i:s" ). " \r\n\r" , FILE_APPEND);

			if (empty($ranking_info_arr)) {
				return true;
			}

			foreach ($ranking_info_arr as $key => $ranking_info) {
				//更改策略状态
				$tactics_ids[] = $ranking_info['tactics_id'];

				//竞拍
				$campaign[$ranking_info['keyword_id']] = array(
						'keyword_id' => $ranking_info['keyword_id'],  //关键词id
						'adgroup_id' => $ranking_info['adgroup_id'],  //推广组id
						'word' => $ranking_info['word'],  //关键词
						'price' => $ranking_info['max_price'],  //当前价格
						'new_price' => $ranking_info['next_price'],  //新价格,
						'matchscope' => $ranking_info['matchscope'],  //'关键词匹配方式',
						'action' => 'adjust'  //标志位
						);

				//竞拍数+1,更新时间戳
				$tactics_id_campaign[] = $ranking_info['tactics_id'];
				$tactics_keyword_iids[] = $ranking_info['keyword_id'];

				//执行成功信息
				$success_data[] = 'tactics_id: '.$ranking_info['tactics_id'].'_keyword_id: '.$ranking_info['keyword_id'].'_success!';
			}

			if($tactics_id_campaign) {
				//执行更新时间戳
				$tactics_id_campaign = array_unique($tactics_id_campaign);
				// $tactics_data['tactics_modified_time'] = time();
				// $tactics_data['doing'] = 0;
				// $tactics_data = array(
				// 		'tactics_modified_time' => time(),
				// 		'doing' => 0
				// 	);
				$this->changeStatusTactics($tactics_id_campaign, array(
														'doing' => 0
													), 'want_rank', $tactics_keyword_iids);
				
				// dump('campaign');
				// dump($campaign);
				// file_put_contents (RUNTIME_PATH.'Logs/'.date("y_m_d").'_mlkr.log' , 'campaign'.' '. date ( "Y-m-d H:i:s" ). " \r\n\r" , FILE_APPEND);
				// file_put_contents (RUNTIME_PATH.'Logs/'.date("y_m_d").'_mlkr.log' , print_r($campaign,true).' '. date ( "Y-m-d H:i:s" ). " \r\n\r" , FILE_APPEND);
				

				//执行竞拍
				$res = R('Keyword/keywordsUpdate', array($campaign), 'Event');

				//执行竞拍数+1
				// $res = 1;
				if($res){
					// $dao = M();
					// $qure_str = implode(',', array_unique($tactics_id_campaign));
					$qure_str = implode(',', $tactics_id_campaign);
					// $sql = "update tactics set executions = executions+1 where tactics_id in ({$qure_str})";
					// if (!$dao->execute($sql)) {
					// 	$e = new Exception("执行竞拍数+1 无记录更新 {$dao->getLastSql()}");
					// 	$this->debug($e);
					// }
					
					$tactics = M('tactics');
					$tactics->where(array('tactics_id' => array('in', $qure_str), 'user_id' => $this->me['user_id']))
							->setInc('executions', 1);
					
					// dump($tactics->getLastSql());
					// file_put_contents (RUNTIME_PATH.'Logs/'.date("y_m_d").'_mlkr.log' , $tactics->getLastSql().' '. date ( "Y-m-d H:i:s" ). " \r\n\r" , FILE_APPEND);
					
					$this->logInfo("完成一次卡位， tactics_id = {$qure_str}, 关键词信息：".json_encode($res) . "成功信息：". json_encode($success_data));

					//写入执行成功信息
					// foreach($success_data as $msg){
					// 	$e = new Exception($msg);
					// 	$this->debug($e);
					// }
					
					//如果是降低排名并且状态为是3,改状态位为1
					foreach ($campaign as $campaign_key => $campaign_value) {
						foreach ($ranking_info_arr as $key => &$ranking_info) {
							if ($campaign_value['keyword_id'] == $ranking_info['keyword_id']) {
								if (preg_match('/^(-\d+)(\.\d+)?\%?$/',$ranking_info['premium'])) {  //负(降低排名)
									if($ranking_info['doing'] == 3){
										$this->changeStatusTactics($ranking_info['tactics_id'], array('doing' => 0), 'want_rank', $ranking_info['keyword_id']);
										$ranking_info['doing'] = 0;
									}
								}
							}
						}
					}

					// dump('third_filter(success)');
					// dump($ranking_info_arr);
					// file_put_contents (RUNTIME_PATH.'Logs/'.date("y_m_d").'_mlkr.log' , 'success'.' '. date ( "Y-m-d H:i:s" ). " \r\n\r" , FILE_APPEND);
					// file_put_contents (RUNTIME_PATH.'Logs/'.date("y_m_d").'_mlkr.log' , print_r($ranking_info_arr,true).' '. date ( "Y-m-d H:i:s" ). " \r\n\r" , FILE_APPEND);
				}
			}

			$tactics_ids = array_unique($tactics_ids);

			// 更改策略状态
			$tactics_data = array(
					'tactics_modified_time' => time()
				);
			// if($k == count($ranking_info_arr))
			// 	$tactics_data['tactics_status'] = 3;
			$this->changeStatusTactics($tactics_ids, $tactics_data);
		}
	}

	//根据执行时间出价过滤卡位策略
	private function _TacticsTimePriceFilter(&$ranking_info_arr)
	{
	    //除执行时间还没到的,格式不正确的,出价过高的
	    $realDoing = 0;
	    $current_time = time();
		foreach ($ranking_info_arr as $key=>&$ranking_info) {
			
			//剔除不在执行区间内的任务
			if($ranking_info['from'] && $ranking_info['to']) {
				if($current_time < $ranking_info['from'] || $current_time >= $ranking_info['to']) {
					unset($ranking_info_arr[$key]);
					continue;
				}
			}

			//判断策略是否在执行中
			$realDoing = $this->getTacticsDoing($ranking_info['tactics_id'], $ranking_info['keyword_id']);
			if($realDoing === 1) {
				unset($ranking_info_arr[$key]);
				continue;
			} else{
				$this->_lock($ranking_info);
			}

			//当前时间-上次执行时间<竞拍周期
			if ($current_time - (int)$ranking_info['tactics_modified_time'] < (int)$ranking_info['campaign_period']) {
				// $this->changeStatusTactics($ranking_info['tactics_id'], array('doing' => 0), 'want_rank', $ranking_info['keyword_id']);
				// $e = new Exception("没到执行时间. tactics_id: {$ranking_info['tactics_id']}");
				// $this->logInfo("没到执行时间. tactics_id: {$ranking_info['tactics_id']} ----". date('Y-m-d H:i:s', $current_time));
				// $this->debug($e);				
				$this->_unlock($ranking_info);
				unset($ranking_info_arr[$key]);
				// 如果还没到时间执行就直接进入下一个账户
				// return true;
				continue;
			}
			
			try{
				//计算得出这次出价
				$ranking_info_arr[$key]['next_price'] = $this->_getNextPrice($ranking_info['max_price'],$ranking_info['premium']);

				/**
				 * 如果是提高出价,要符合以下出价规则
				 * 新出价 > 最高出价 && 当前出价 >= 最高出价   	设置最高出价状态(doing = 3)  
				 * 新出价 > 最高出价 && 当前出价 < 最高出价		新出价 = 最高出价
				 */
				if(!preg_match('/^(-\d+)(\.\d+)?\%?$/',$ranking_info['premium'])) {  //正(提高排名)
					// 如果这个词是最高出价状态则不进行卡位
					if($ranking_info['doing'] === 3) {
						unset($ranking_info_arr[$key]);
						continue;
					}else if ($ranking_info['heighest_price'] < $ranking_info_arr[$key]['next_price'] && $ranking_info['max_price'] >= $ranking_info['heighest_price']) {
						$this->changeStatusTactics($ranking_info['tactics_id'], array('doing' => 3, 'msg'=> "当前出价大于最高限价，限价：".($ranking_info['heighest_price']/100)." 元", 'update_time_keyword' => time()), 'want_rank', $ranking_info['keyword_id']);
						$this->logInfo("下次出价大于最高限价. tactics_id: {$ranking_info['tactics_id']}. keyword_id: {$ranking_info['keyword_id']}");
						unset($ranking_info_arr[$key]);
						continue;
					} else if($ranking_info['heighest_price'] < $ranking_info_arr[$key]['next_price'] && $ranking_info['max_price'] < $ranking_info['heighest_price']) {
						$ranking_info_arr[$key]['next_price'] = $ranking_info['heighest_price'];
					}
				}else{// 负(降低排名)
					//去除出价为负的
					if($ranking_info['doing'] == 2){
						unset($ranking_info_arr[$key]);
						continue;
					}
				}
			}catch(Exception $e){
				$this->changeStatusTactics($ranking_info['tactics_id'], array('doing' => "{$e->getCode()}", 'msg' => "{$e->getMessage()}", 'update_time_keyword' => time()), 'want_rank', $ranking_info['keyword_id']);
				$this->ranking->printError($e);
				$this->logError($e->getMessage());
				$this->logException($e, true);
				$this->_unlock($ranking_info);
				unset($ranking_info_arr[$key]);
				continue;
			}
		}
		// if(!$ranking_info_arr) return true;
	}

	//过滤排名不合适的卡位策略
	private function _TacticsRankFilter(&$ranking_info_arr)
	{
		//获取现在排名(100以外排名为0)
		$ranking_info_arr = $this->ranking->getCurrentRank($ranking_info_arr);

		$k = 0; // 已经超过排名个数
		foreach ($ranking_info_arr as $key => &$ranking_info) {

			if(!$ranking_info['want_rank']){  //没有设置期望排名
				$this->_unlock($ranking_info);
				unset($ranking_info_arr[$key]);
				continue;
			}

			if ($this->_section($ranking_info['rank'],$ranking_info['want_rank'],0)) {  //在此区间不进行卡位
				$this->_unlock($ranking_info);
				unset($ranking_info_arr[$key]);
				continue;
			}

			if(!preg_match('/^(-\d+)(\.\d+)?\%?$/',$ranking_info['premium'])) {  //正(提高排名)
				if((int)$ranking_info['want_rank'] >= (int)$ranking_info['rank'] && (int)$ranking_info['rank'] !== 0 ){  //现在排名提高到或已超过期望排名
					$k++;
					$this->changeStatusTactics($ranking_info['tactics_id'], array('doing' => 0), 'want_rank', $ranking_info['keyword_id']);
					$this->_unlock($ranking_info);
					unset($ranking_info_arr[$key]);
					continue;
				}
			}else{  //负(降低排名)
				if((int)$ranking_info['want_rank'] <= (int)$ranking_info['rank'] || (int)$ranking_info['rank'] === 0 ){  //现在排名下降到或已超过期望排名
					$k++;
					$this->changeStatusTactics($ranking_info['tactics_id'], array('doing' => 0), 'want_rank', $ranking_info['keyword_id']);
					$this->_unlock($ranking_info);
					unset($ranking_info_arr[$key]);
					continue;
				}
			}
		}
		// if(!$ranking_info_arr) return false;
	}

	//在这个排名区间的,不出价排名
	//[-sectionNum,sectionNum] 在这个区间里的不出价排名
	private function _section($sour,$obj,$sectionNum)
	{
		if(!preg_match('/^(\d+)(\.\d+)?\%?$/', $sour)) return false;
		if(!preg_match('/^(\d+)(\.\d+)?\%?$/', $obj)) return false;
		if(!preg_match('/^(\d+)(\.\d+)?\%?$/', $sectionNum)) return false;

		$absValue = abs((int)$sour - (int)$obj);
		if ((int)$sectionNum >= (int)$absValue) {
			return true;
		}else{
			return false;
		}
	}

	private function _lock(&$ranking_info)
	{
		if((int)$ranking_info['doing'] === 0){
			$this->changeStatusTactics($ranking_info['tactics_id'], array('doing' => 1), 'want_rank', $ranking_info['keyword_id']);
			$ranking_info['doing'] = 1;
		}
	}

	private function _unlock(&$ranking_info)
	{
		if ((int)$ranking_info['doing'] === 1) {
			$this->changeStatusTactics($ranking_info['tactics_id'], array('doing' => 0), 'want_rank', $ranking_info['keyword_id']);
			$ranking_info['doing'] = 0;
		}
	}


	/**
	 * 计算下次出价
	 * @param  float $value   现在出价
	 * @param  string $premium 溢价
	 * @return float          下次出价
	 */
	private function _getNextPrice($value,$premium)
	{
		$nextPrice = -1;

		//检查格式
		if(!preg_match('/^([+]?\d+)(\.\d+)?$/',$value) || is_null($value)){
			throw new Exception("value格式错误", 4);
		}
		if(!preg_match('/^([-+]?\d+)(\.\d+)?\%?$/',$premium) || is_null($premium)){  
			throw new Exception("premium格式错误", 4);
		}

		//计算
		if (preg_match('/%/',$premium)) { 
		    //有%
		    $premium = substr ( "$premium" ,  0 , - 1 );
			$nextPrice = round($value + $value * $premium);
			
		}else{
			//无%
			$nextPrice = round($value + $premium);
		}

		if ($nextPrice > 0) {
			return $nextPrice;
		}else{
			throw new Exception("下次出价为负", 2);		
		}
	}

	function debug(Exception $e)
	{
		$this->ranking->printError($e);
	}
}


